/*
 *
 * (c) Copyright Ascensio System Limited 2010-2023
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

using ASC.Api.Attributes;
using ASC.Api.Documents;
using ASC.Api.Projects.Wrappers;
using ASC.Api.Utils;
using ASC.MessagingSystem;
using ASC.Projects.Core.Domain;
using ASC.Projects.Core.Domain.Reports;
using ASC.Projects.Engine;
using ASC.Specific;
using ASC.Web.Core.Files;
using ASC.Web.Files.Services.DocumentService;
using ASC.Web.Projects.Classes;

namespace ASC.Api.Projects
{
    public partial class ProjectApi
    {
        /// <summary>
        /// Checks the report data by the URI specified in the request.
        /// </summary>
        /// <short>
        /// Check the report data
        /// </short>
        /// <param type="System.String, System" name="uri">Report URI</param>
        /// <returns type="ASC.Web.Files.Services.DocumentService.ReportState, ASC.Web.Files">Report status</returns>
        /// <path>api/2.0/project/report/create</path>
        /// <category>Reports</category>
        /// <httpMethod>POST</httpMethod>
        [Create(@"report/create")]
        public ReportState CheckReportData(string uri)
        {
            ChechDocBuilder();

            ReportState state;

            Report.TryCreateReport(uri.Split('?')[1], out state);

            return state;
        }

        /// <summary>
        /// Returns a report status.
        /// </summary>
        /// <short>
        /// Get a report status
        /// </short>
        /// <category>Reports</category>
        /// <returns type="ASC.Web.Files.Services.DocumentService.ReportState, ASC.Web.Files">Report status</returns>
        /// <path>api/2.0/project/report/status</path>
        /// <httpMethod>GET</httpMethod>
        [Read(@"report/status")]
        public ReportState GetReportStatus()
        {
            ChechDocBuilder();

            return DocbuilderReportsUtility.Status(ReportOrigin.Projects);
        }

        /// <summary>
        /// Terminates the reporting process.
        /// </summary>
        /// <short>
        /// Terminate the reporting process
        /// </short>
        /// <category>Reports</category>
        /// <path>api/2.0/project/report/terminate</path>
        /// <httpMethod>GET</httpMethod>
        /// <returns></returns>
        [Read(@"report/terminate")]
        public void TerminateReport()
        {
            ChechDocBuilder();

            DocbuilderReportsUtility.Terminate(ReportOrigin.Projects);
        }

        /// <summary>
        /// Creates a project report template with the parameters specified in the request. Most of the parameters are optional and depend on the report type selected.
        /// </summary>
        /// <short>
        /// Create a report template
        /// </short>
        /// <category>Reports</category>
        /// <param type="System.String, System" name="name">Report name</param>
        /// <param type="System.String, System" name="period" optional="true">Report generation period: Day/Week/Month</param>
        /// <param type="System.Int32, System" name="periodItem" optional="true">Period item: day of the week (for a weekly report, starting with Sunday), day of the month (for a monthly report)</param>
        /// <param type="System.Int32, System" name="hour" optional="true">Report sending time (in hours)</param>
        /// <param type="System.Boolean, System" name="autoGenerated" optional="true">Specifies if a report will be automatically generated or not</param>
        /// <param type="ASC.Projects.Core.Domain.Reports.ReportType, ASC.Projects.Core.Domain.Reports" name="reportType" optional="true">Report type</param>
        /// <param type="System.Int32, System" name="tag" optional="true">Report tag</param>
        /// <param type="System.Int32, System" name="project" optional="true">Report project</param>
        /// <param type="System.Nullable{ASC.Projects.Core.Domain.TaskStatus}, System" name="status" optional="true">Task status</param>
        /// <param type="System.Guid, System" name="departament" optional="true">Department/Group GUID</param>
        /// <param type="System.Guid, System" name="userId" optional="true">User GUID</param>
        /// <param type="ASC.Projects.Core.Domain.Reports.ReportTimeInterval, ASC.Projects.Core.Domain.Reports" name="reportTimeInterval" optional="true">Report time interval</param>
        /// <param type="ASC.Specific.ApiDateTime, ASC.Specific" name="fromDate" optional="true">Report period start date</param>
        /// <param type="ASC.Specific.ApiDateTime, ASC.Specific" name="toDate" optional="true">Report period end date</param>
        /// <param type="System.Int32, System" name="viewType" optional="true">Report view type: by group or by project</param>
        /// <param type="System.Boolean, System" name="noResponsible" optional="true">Specifies if the tasks without responsibles will be shown or hidden</param>
        /// <param type="System.Boolean, System" name="isShowAverageTime"  optional="true">Specifies if the average time will be shown or not</param>
        /// <param type="ASC.Projects.Core.Domain.AverageTime, ASC.Projects.Core.Domain" name="typeOfShowAverageTime" optional="true">Average time type (All, ClosingProjects, CompletingTasks)</param>
        /// <param type="System.Boolean, System" name="projectAverageCompletingTasks" optional="true">Specifies if the average time of completing tasks will be shown or not</param>
        /// <returns type="ASC.Api.Projects.Wrappers.ReportTemplateWrapper, ASC.Api.Projects">Project report template</returns>
        /// <path>api/2.0/project/report</path>
        /// <httpMethod>POST</httpMethod>
        [Create(@"report")]
        public ReportTemplateWrapper SaveReportTemplate(
            string name,
            string period,
            int periodItem,
            int hour,
            bool autoGenerated,
            ReportType reportType,
            int tag,
            int project,
            TaskStatus? status,
            Guid departament,
            Guid userId,
            ReportTimeInterval reportTimeInterval,
            ApiDateTime fromDate,
            ApiDateTime toDate,
            int viewType,
            bool noResponsible,
            bool isShowAverageTime,
            AverageTime typeOfShowAverageTime,
            bool projectAverageCompletingTasks
            )
        {
            ProjectSecurity.DemandAuthentication();

            if (name == null || name.Trim().Length == 0) throw new ArgumentNullException("name");

            var filter = new TaskFilter
            {
                TagId = tag,
                DepartmentId = departament,
                UserId = userId,
                TimeInterval = reportTimeInterval,
                FromDate = fromDate,
                ToDate = toDate,
                ViewType = viewType,
                NoResponsible = noResponsible,
                IsShowAverageTime = isShowAverageTime,
                TypeOfShowAverageTime = typeOfShowAverageTime
            };
            if (projectAverageCompletingTasks)
            {
                filter.IsShowAverageTime = true;
                filter.TypeOfShowAverageTime = AverageTime.CompletingTasks;
            }

            if (project != 0)
            {
                filter.ProjectIds.Add(project);
            }

            if (status != null)
            {
                filter.TaskStatuses.Add((TaskStatus)status);
            }

            var template = new ReportTemplate(reportType) { Filter = filter };

            SaveOrUpdateTemplate(template, name, period, periodItem, hour, autoGenerated);
            MessageService.Send(Request, MessageAction.ReportTemplateCreated, MessageTarget.Create(template.Id), template.Name);

            return new ReportTemplateWrapper(template);
        }

        /// <summary>
        /// Updates the selected project report template with the parameters specified in the request.
        /// </summary>
        /// <short>
        /// Update a report template
        /// </short>
        /// <category>Reports</category>
        /// <param type="System.Int32, System" method="url" name="reportid">Report template ID</param>
        /// <param type="System.String, System" name="name">New report name</param>
        /// <param type="System.String, System" name="period" optional="true">New report generation period: Day/Week/Month</param>
        /// <param type="System.Int32, System" name="periodItem" optional="true">New period item: day of the week (for a weekly report, starting with Sunday), day of the month (for a monthly report)</param>
        /// <param type="System.Int32, System" name="hour" optional="true">New report sending time (in hours)</param>
        /// <param type="System.Boolean, System" name="autoGenerated" optional="true">Specifies if a report will be automatically generated or not</param>
        /// <param type="ASC.Projects.Core.Domain.Reports.ReportType, ASC.Projects.Core.Domain.Reports" name="reportType" optional="true">New report type</param>
        /// <param type="System.Int32, System" name="tag" optional="true">New report tag</param>
        /// <param type="System.Int32, System" name="project" optional="true">New report project</param>
        /// <param type="System.Nullable{ASC.Projects.Core.Domain.TaskStatus}, System" name="status" optional="true">New task status</param>
        /// <param type="System.Guid, System" name="departament" optional="true">New department/group GUID</param>
        /// <param type="System.Guid, System" name="userId" optional="true">New user GUID</param>
        /// <param type="ASC.Projects.Core.Domain.Reports.ReportTimeInterval, ASC.Projects.Core.Domain.Reports" name="reportTimeInterval" optional="true">New report time interval</param>
        /// <param type="ASC.Specific.ApiDateTime, ASC.Specific" name="fromDate" optional="true">New report period start date</param>
        /// <param type="ASC.Specific.ApiDateTime, ASC.Specific" name="toDate" optional="true">New report period end date</param>
        /// <param type="System.Int32, System" name="viewType" optional="true">New view type: by group or by project</param>
        /// <param type="System.Boolean, System" name="noResponsible" optional="true">Specifies if the tasks without responsibles will be shown or hidden</param>
        /// <returns type="ASC.Api.Projects.Wrappers.ReportTemplateWrapper, ASC.Api.Projects">Updated project report template</returns>
        /// <path>api/2.0/project/report/{reportid}</path>
        ///<httpMethod>PUT</httpMethod>
        [Update(@"report/{reportid:[0-9]+}")]
        public ReportTemplateWrapper UpdateReportTemplate(
            int reportid,
            string name,
            string period,
            int periodItem,
            int hour,
            bool autoGenerated,
            ReportType reportType,
            int tag,
            int project,
            TaskStatus? status,
            Guid departament,
            Guid userId,
            ReportTimeInterval reportTimeInterval,
            ApiDateTime fromDate,
            ApiDateTime toDate,
            int viewType,
            bool noResponsible)
        {
            ProjectSecurity.DemandAuthentication();

            var filter = new TaskFilter
            {
                TagId = tag,
                DepartmentId = departament,
                UserId = userId,
                TimeInterval = reportTimeInterval,
                FromDate = fromDate,
                ToDate = toDate,
                ViewType = viewType,
                NoResponsible = noResponsible
            };

            if (project != 0)
            {
                filter.ProjectIds.Add(project);
            }

            if (status != null)
            {
                filter.TaskStatuses.Add((TaskStatus)status);
            }

            var template = EngineFactory.ReportEngine.GetTemplate(reportid);
            template.Filter = filter;

            SaveOrUpdateTemplate(template, name, period, periodItem, hour, autoGenerated);
            MessageService.Send(Request, MessageAction.ReportTemplateUpdated, MessageTarget.Create(template.Id), template.Name);

            return new ReportTemplateWrapper(template);
        }

        /// <summary>
        /// Returns a project report template with the ID specified in the request.
        /// </summary>
        /// <short>
        /// Get a report template
        /// </short>
        /// <category>Reports</category>
        /// <param type="System.Int32, System" method="url" name="reportid">Report template ID</param>
        /// <returns type="ASC.Api.Projects.Wrappers.ReportTemplateWrapper,  ASC.Api.Projects">Project report template</returns>
        /// <path>api/2.0/project/report/{reportid}</path>
        /// <httpMethod>GET</httpMethod>
        [Read(@"report/{reportid:[0-9]+}")]
        public ReportTemplateWrapper GetReportTemplate(int reportid)
        {
            ProjectSecurity.DemandAuthentication();
            return new ReportTemplateWrapper(EngineFactory.ReportEngine.GetTemplate(reportid).NotFoundIfNull());
        }

        /// <summary>
        /// Deletes a project report template with the ID specified in the request.
        /// </summary>
        /// <short>
        /// Delete a report template
        /// </short>
        /// <category>Reports</category>
        /// <param type="System.Int32, System" method="url" name="reportid">Report template ID</param>
        /// <returns type="ASC.Api.Projects.Wrappers.ReportTemplateWrapper, ASC.Api.Projects">Project report template</returns>
        /// <path>api/2.0/project/report/{reportid}</path>
        /// <httpMethod>DELETE</httpMethod>
        [Delete(@"report/{reportid:[0-9]+}")]
        public ReportTemplateWrapper DeleteReportTemplate(int reportid)
        {
            ProjectSecurity.DemandAuthentication();

            var reportEngine = EngineFactory.ReportEngine;

            var reportTemplate = reportEngine.GetTemplate(reportid).NotFoundIfNull();

            reportEngine.DeleteTemplate(reportid);
            MessageService.Send(Request, MessageAction.ReportTemplateDeleted, MessageTarget.Create(reportTemplate.Id), reportTemplate.Name);

            return new ReportTemplateWrapper(reportTemplate);
        }

        /// <summary>
        /// Returns the generated report files.
        /// </summary>
        /// <short>
        /// Get generated reports
        /// </short>
        /// <category>Reports</category>
        /// <returns type="ASC.Api.Documents.FileWrapper, ASC.Api.Documents">Generated report files</returns>
        /// <path>api/2.0/project/report/files</path>
        /// <httpMethod>GET</httpMethod>
        /// <collection>list</collection>
        [Read(@"report/files")]
        public IEnumerable<FileWrapper> GetGeneratedReports()
        {
            ChechDocBuilder();

            var fileIds = EngineFactory.ReportEngine.Get()
                .Select(r => r.FileId)
                .ToList();

            return EngineFactory.FileEngine.GetFiles(fileIds).Select(r => new FileWrapper(r)).OrderByDescending(r => r.Id).ToList();
        }

        /// <summary>
        /// Removes the generated report file with the ID specified in the request.
        /// </summary>
        /// <short>
        /// Remove the generated report
        /// </short>
        /// <category>Reports</category>
        /// <param type="System.Int32, System" method="url" name="fileid">Report file ID</param>
        /// <returns type="ASC.Projects.Core.Domain.Reports.ReportFile, ASC.Web.Projects">Generated report file</returns>
        /// <path>api/2.0/project/report/files/{fileid}</path>
        /// <httpMethod>DELETE</httpMethod>
        [Delete(@"report/files/{fileid:[0-9]+}")]
        public ReportFile RemoveGeneratedReport(int fileid)
        {
            ProjectSecurity.DemandAuthentication();

            var reportEngine = EngineFactory.ReportEngine;

            var report = reportEngine.GetByFileId(fileid).NotFoundIfNull();

            reportEngine.Remove(report);

            return report;
        }

        private void ChechDocBuilder()
        {
            ProjectSecurity.DemandAuthentication();
            if (string.IsNullOrEmpty(FilesLinkUtility.DocServiceDocbuilderUrl))
                throw ProjectSecurity.CreateSecurityException();
        }

        private void SaveOrUpdateTemplate(ReportTemplate template, string title, string period, int periodItem, int hour, bool autoGenerated)
        {
            template.Name = HttpUtility.HtmlEncode(title);
            switch (period)
            {
                case "day":
                    template.Cron = string.Format("0 0 {0} * * ?", hour);
                    break;
                case "week":
                    template.Cron = string.Format("0 0 {0} ? * {1}", hour, periodItem);
                    break;
                case "month":
                    template.Cron = string.Format("0 0 {0} {1} * ?", hour, periodItem);
                    break;
                default:
                    template.Cron = string.Format("0 0 {0} * * ?", 12);
                    break;
            }
            template.AutoGenerated = autoGenerated;
            EngineFactory.ReportEngine.SaveTemplate(template);
        }
    }
}